package netty.aio;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @program NettyLeaning
 * @description:
 * @author: zhanglu
 * @create: 2022-01-17 22:35:00
 */
@Slf4j
public class TimeServer {

    private static final String IP = "127.0.0.1";
    private static final Integer PORT = 8080;

    @Test
    public void server() throws Exception {
        AsyncTimeServerHandler asyncTimeServerHandler = new AsyncTimeServerHandler(PORT);
        new Thread(asyncTimeServerHandler).start();
        while (true){
            TimeUnit.MICROSECONDS.sleep(100);
        }
    }

    public class AsyncTimeServerHandler implements Runnable{

        private AsynchronousServerSocketChannel asynchronousServerSocketChannel;
        private CountDownLatch latch;

        public AsyncTimeServerHandler(Integer port) throws Exception {
            asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
            asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
            log.info("server is start port :" + PORT);
        }

        @Override
        public void run() {
            latch = new CountDownLatch(1);
            asynchronousServerSocketChannel.accept(this, new AcceptCompletionHandler());
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncTimeServerHandler> {
        @Override
        public void completed(AsynchronousSocketChannel result, AsyncTimeServerHandler attachment) {
            //新的客户端接入
            attachment.asynchronousServerSocketChannel.accept(attachment, this);
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            result.read(buffer, buffer, new ReadCompletionHandler(result));
        }

        @Override
        public void failed(Throwable exc, AsyncTimeServerHandler attachment) {
            attachment.latch.countDown();
        }
    }

    public class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {
        private AsynchronousSocketChannel channel;
        public ReadCompletionHandler(AsynchronousSocketChannel channel){
            if(this.channel == null){
                this.channel = channel;
            }
        }
        @Override
        public void completed(Integer result, ByteBuffer attachment) {
            attachment.flip();
            byte[] body = new byte[attachment.remaining()];
            attachment.get(body);
            try {
                String req = new String(body, CharsetUtil.UTF_8);
                log.info("server receive : "+ req);
                String currentTime = DateUtil.formatDateTime(new Date());
                byte[] bodyBytes = currentTime.getBytes();
                ByteBuffer writeBuffer = ByteBuffer.allocate(bodyBytes.length);
                writeBuffer.put(bodyBytes);
                writeBuffer.flip();
                channel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, ByteBuffer buffer) {
                        //如果没有发送完成，继续发送
                        if(buffer.hasRemaining()){
                            channel.write(buffer, buffer, this);
                        }
                    }
                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {
                        try {
                            channel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }


        }

        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {
            try {
                this.channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}
